window.py = {}


var Helper = {
    extend:function (obj1, obj2) {
        if (!obj1) {
            obj1 = {}
        }

        if (obj2) {
            for (var prop in obj2) {
                obj1[prop] = obj2[prop];
            }
        }

        return obj1;
    },

    // move to math.js
//        distance:function (p1, p2) {
//            return {x:p2.x - p1.x, y:p2.y - p1.y};
//        },

    isFunction:function (object) {
        return object && Object.prototype.toString.call(object) == '[object Function]';
    },

    isArray:function (obj) {
        return Object.prototype.toString.call(obj) === '[object Array]'
    },

    inArray:function (obj, array) {
        if (!Helper.isArray(array)) {
            return false;
        }
        return array.indexOf(obj) >= 0;
    },

    removeFromArray:function (obj, array) {
        var index;
        if (!Helper.isArray(array)) {
            return;
        }

        index = array.indexOf(obj);
        return array.splice(index, 1);
    },

    foreach:function (array, callback) {
        var length = 0, i = 0;
        if (!Helper.isArray(array) || !Helper.isFunction(callback)) {
            return;
        }

        length = array.length;
        for (; i < length; ++i) {
            callback(array[i], i);
        }
    },

    // move to math.js
//        addVector:function (a, b) {
//            return {x:a.x + b.x, y:a.y + b.y};
//        },

    toProper:function (str, truncate) {
        var temp = (" " + str).toLowerCase().replace(/\s(\S)/g,
            function ($1) {
                return $1.toUpperCase()
            }).substr(1);
        return truncate ? temp.replace('\s', '') : temp;
    },

    isBoolean:function (b) {
        return b === true || b === false;
    },

    Logger:(new function () {

        var self = this;
        this.debug = true;
        this.showStack = false;

        this.log = function () {
            return;
            var stack;
            if (self.debug) {
                if (self.showStack) {
                    try {
                        throw new Error('stack trace:');
                    } catch (e) {
                        return console.log(arguments, e.stack);
                    }
                }
                console.log(arguments);
            }
        }

        window.py.log = this.log;
    }),

    abstractMethod:function () {
        return function () {
            throw new Error('this method is not implemented');
        };
    },

    using:function (ns, code) {

        var array, currentContext = window;

        if (typeof ns != 'string') {
            throw new Error('namespace must be a string');
        }

        array = ns.split('.');
        array.forEach(function (context) {
            var c = currentContext[context];
            if (c) {
                currentContext = c;
            } else {
                currentContext = currentContext[context] = {};
            }
        });

        if (code && Helper.isFunction(code)) {
            code.call(currentContext, currentContext);

        }

    },

    throttle:function (fn, delay) {
        var timer = null;
        return function () {
            var context = this, args = arguments;
            clearTimeout(timer);
            timer = setTimeout(function () {
                fn.apply(context, args);
            }, delay);
        };
    },

    guid:function () {
        var s = [], itoh = '0123456789ABCDEF';

        // Make array of random hex digits. The UUID only has 32 digits in it, but we
        // allocate an extra items to make room for the '-'s we'll be inserting.
        for (var i = 0; i < 36; i++) s[i] = Math.floor(Math.random() * 0x10);

        // Conform to RFC-4122, section 4.4
        s[14] = 4;  // Set 4 high bits of time_high field to version
        s[19] = (s[19] & 0x3) | 0x8;  // Specify 2 high bits of clock sequence

        // Convert to hex chars
        for (var i = 0; i < 36; i++) s[i] = itoh[s[i]];

        // Insert '-'s
        s[8] = s[13] = s[18] = s[23] = '-';

        return s.join('');
    }
};

Array.prototype.remove = Array.prototype.remove || function (obj) {
    return Helper.removeFromArray.call(this, obj, this);
}

Array.prototype.forEach = Array.prototype.forEach || function (callback) {
    return Helper.foreach(this, callback);
}

String.prototype.toProperCase = function () {
    return Helper.toProper(this, arguments);
}

Function.prototype.throttle = function (delay) {
    return Helper.throttle(this, delay);
}

Kinetic.Stage.prototype.getCenter = function () {
    return {
        x:this.getWidth() / 2, y:this.getHeight() / 2
    };
}


py.logTime = function () {
    var time = new Date().getTime()
    py.log('Time: ' + time);
    return time;
}


window._ = window._ || Helper;


//Copyright (c) 2010 Nicholas C. Zakas. All rights reserved.
//MIT License
function EventTarget() {
    this._listeners = {};
    this.on = function (type, listener) {
        if (typeof this._listeners[type] == "undefined") {
            this._listeners[type] = [];
        }

        this._listeners[type].push(listener);
        return this;
    };

    this.fire = function (event) {
        if (typeof event == "string") {
            event = { type:event };
        }
        if (!event.target) {
            event.target = this;
        }

        if (!event.type) {  //falsy
            throw new Error("Event object missing 'type' property.");
        }

        if (this._listeners[event.type] instanceof Array) {
            var listeners = this._listeners[event.type];
            for (var i = 0, len = listeners.length; i < len; i++) {
                listeners[i].call(this, event);
            }
        }
    }

    this.off = function (type, listener) {
        if (this._listeners[type] instanceof Array) {

            if (listener === true) {
                this._listeners[type] = [];
                return;
            }

            var listeners = this._listeners[type];
            for (var i = 0, len = listeners.length; i < len; i++) {
                if (listeners[i] === listener) {
                    listeners.splice(i, 1);
                    break;
                }
            }
        }
    }
}


var ObservableArray = function (initArray) {
    var _array = initArray || [];
    EventTarget.call(_array);

    _array.push = function () {
        this.fire({type:"pushing", data:arguments });
        Array.prototype.push.apply(_array, arguments);
        this.fire({type:"pushed", data:arguments });
    }

    _array.remove = function () {
        this.fire({type:"removing", data:arguments });
        Array.prototype.remove.apply(_array, arguments);
        this.fire({type:"removed", data:arguments });
    }

    return _array;
}

var Bound = function (params) {
    var n = NaN;
    params = Helper.extend({
        x:n, y:n, w:n, h:n
    }, params);

    this.getX = function () {
        return params.x;
    };
    this.getY = function () {
        return params.y;
    };
    this.getW = function () {
        return params.w;
    };
    this.getH = function () {
        return params.h;
    };
    this.getBounds = function () {
        return [
            {x:params.x, y:params.y},
            {x:params.x + params.w, y:params.y + params.h}
        ];
    };
};